package com.dbms;

import com.dbms.common.Column;
import com.dbms.common.QueryResult;
import com.dbms.common.Tuple;
import com.dbms.DbmsCore;
import java.io.File;

/**
 * 数据库管理系统(DBMS)并发控制功能的独立测试程序
 * 功能：模拟"丢失更新"场景，验证DBMS的页面级锁定机制能否防止并发异常
 * 场景说明：两个并发事务同时基于旧值修改同一份数据，正确的锁机制应保证最终结果准确
 * 【已修正】使用独立的数据库文件和日志文件，避免影响其他测试或数据
 */
public class ConcurrencyTest {

    // 测试专用的数据库文件名称，独立于其他测试数据
    private static final String DB_FILE_NAME = "concurrency_test.db";
    // 测试专用的日志文件名称，用于记录事务日志
    private static final String LOG_FILE_NAME = "concurrency_test.log";


    /**
     * 程序主入口，执行并发控制测试的完整流程
     * 流程包括：环境准备、并发事务执行、结果验证
     * @param args 命令行参数（未使用）
     */
    public static void main(String[] args) {
        System.out.println("============== 并发控制功能演示开始 ==============");
        System.out.println("本测试将模拟两个用户同时对同一个银行账户进行存取款操作。");
        System.out.println("如果没有正确的锁机制，这将会导致“丢失更新”问题。\n");

        // --- 1. 准备阶段：清理环境并创建初始数据 ---
        System.out.println("--- 1. 正在设置测试环境 ---");
        // 删除旧的测试文件，确保每次测试环境干净
        new File(DB_FILE_NAME).delete();
        new File(LOG_FILE_NAME).delete();

        // 【核心修正】使用带文件名的构造函数，创建独立的DBMS实例，避免干扰其他数据
        final DbmsCore dbmsCore = new DbmsCore(DB_FILE_NAME, LOG_FILE_NAME);

        System.out.println("正在创建 'accounts' 表并插入初始记录...");
        // 创建账户表（包含id和balance字段）
        dbmsCore.processSql("CREATE TABLE accounts (id INT PRIMARY KEY, balance INT);");
        // 插入初始数据：账户ID=1，初始余额1000
        dbmsCore.processSql("INSERT INTO accounts VALUES (1, 1000);");
        System.out.println("初始状态：账户ID为 1 的余额为 1000。");
        System.out.println("--------------------------------------------------\n");

        // --- 2. 并发执行阶段 ---
        System.out.println("--- 2. 启动两个并发事务 ---");
        System.out.println("事务1 (Thread-1) 将执行 'UPDATE accounts SET balance = balance + 200'");
        System.out.println("事务2 (Thread-2) 将执行 'UPDATE accounts SET balance = balance - 100'");
        System.out.println("最终正确的余额应该是 1000 + 200 - 100 = 1100。\n");

        // 线程1：模拟第一个用户的事务（存款200）
        Thread thread1 = new Thread(() -> {
            System.out.println("[线程1] 启动事务，准备存入 200...");
            // 执行更新操作：余额增加200
            dbmsCore.processSql("UPDATE accounts SET balance = balance + 200 WHERE id = 1;");
            System.out.println("[线程1] 事务已提交。");
        });

        // 线程2：模拟第二个用户的事务（取款100）
        Thread thread2 = new Thread(() -> {
            System.out.println("[线程2] 启动事务，准备取出 100...");
            // 执行更新操作：余额减少100
            dbmsCore.processSql("UPDATE accounts SET balance = balance - 100 WHERE id = 1;");
            System.out.println("[线程2] 事务已提交。");
        });

        // 启动两个线程，模拟并发执行
        thread1.start();
        thread2.start();

        // 等待两个线程执行完毕（确保主线程在验证结果前等待所有事务完成）
        try {
            thread1.join();  // 等待线程1结束
            thread2.join();  // 等待线程2结束
        } catch (InterruptedException e) {
            e.printStackTrace();  // 处理线程中断异常
        }

        // --- 3. 验证阶段 ---
        System.out.println("\n--- 3. 验证最终结果 ---");
        System.out.println("最终查询结果: ");
        // 【核心修正】执行查询并捕获结果，而非仅执行SQL
        QueryResult finalResult = dbmsCore.processSql("SELECT * FROM accounts WHERE id = 1;");
        // 打印查询结果供验证
        printResult(finalResult);
        // --- 【修正结束】 ---

        System.out.println("\n结果分析：由于UPDATE操作需要获取页面写锁，第一个成功获取锁的事务会执行并提交。");
        System.out.println("第二个事务在尝试获取同一个页面的写锁时会被阻塞，直到第一个事务释放锁。");
        System.out.println("这样就保证了两个修改操作被串行化执行，最终得到正确的结果，避免了“丢失更新”问题。");
        System.out.println("============== 演示结束 ==============");

        // 关闭DBMS核心，释放资源
        dbmsCore.shutdown();
    }

    /**
     * 【新增】辅助方法：将查询结果格式化并打印到控制台
     * 处理成功/失败两种情况，清晰展示查询结果
     * @param result 要打印的查询结果对象，包含执行状态、数据和消息
     */
    private static void printResult(QueryResult result) {
        if (result.isSuccess) {  // 处理查询成功的情况
            if (result.schema != null && !result.tuples.isEmpty()) {
                // 打印表头（列名）
                for (Column col : result.schema.getColumns()) {
                    System.out.print(col.getColumnName() + "\t | ");
                }
                System.out.println("\n--------------------");

                // 打印每一行数据
                for (Tuple tuple : result.tuples) {
                    System.out.println(tuple.toString());
                }
            }
            // 打印成功消息（适用于无数据返回的情况，如UPDATE/INSERT的成功提示）
            if (result.message != null && result.tuples.isEmpty()) {
                System.out.println(result.message);
            }
        } else {  // 处理查询失败的情况
            System.err.println("ERROR: " + result.message);  // 打印错误信息
        }
    }
}